package com.upb.webserver.appmarket;

import cn.hutool.core.collection.CollectionUtil;
import com.upb.webserver.common.pojo.appmarket.AppContract;
import com.upb.webserver.common.pojo.appmarket.AppDocument;
import com.upb.webserver.common.pojo.appmarket.AppInfo;
import com.upb.webserver.common.pojo.appmarket.AppRemarkConfig;
import com.upb.webserver.common.properties.WebServerProperties;
import com.upb.webserver.common.tools.JsonUtils;
import com.upb.webserver.common.tools.WebServerTools;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;

import java.io.File;
import java.io.FileNotFoundException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class AppMarketService {
    @Autowired
    private WebServerProperties config;
    private static final String CONTRACTS_DIRECTORY_NAME = "contracts";
    private static final String DOCUMENTS_DIRECTORY_NAME = "documents";
    private static final String LOGO_FILE_NAME = "logo.png";
    private static final String REMARK_JSON_FILE_NAME = "remark.json";

    /**
     * 获取配置的应用市场信息。
     *
     * @return
     */
    public List<AppInfo> getAllAppInfo() {
        List<File> appFileList = getAllAppFileFromMarketConfig();//获取所有应用配置
        if (CollectionUtil.isEmpty(appFileList)) {
            log.warn("There are no examples in the app market");
            return null;
        }

        List<AppInfo> appInfoList = new ArrayList<>();
        for (File appFile : appFileList) {
            if (isAppInfoAllConfigSuccess(appFile))
                appInfoList.add(getAppInfoFromFile(appFile));
        }

        return appInfoList;
    }

    /**
     * 根据应用名称获取合约列表
     *
     * @param appName
     * @return
     */
    public List<AppContract> getAllContractByAppName(String appName) {
        // get contract form specified app
        List<AppContract> appContractList = new ArrayList<>();
        File contractDirectoryFile = getContractDirectoryByAppName(appName);
        if (null == contractDirectoryFile) {
            log.warn("not found contract config of app:{}", appName);
            return appContractList;
        }

        //get AppContract from file
        for (File contractFile : contractDirectoryFile.listFiles()) {
            if (!contractFile.isFile())
                continue;
            AppContract appContract = new AppContract();
            appContract.setName(contractFile.getName());
            appContract.setSourceBase64(WebServerTools.convertFileToBase64(contractFile));
            appContractList.add(appContract);
        }

        return appContractList;
    }


    /**
     * 根据应用名称获取应用下面的合约目录。
     *
     * @param appName
     * @return
     */
    public File getContractDirectoryByAppName(String appName) {
        //get specified file by name
        File specifiedAppFile = getSpecifiedAppFileByName(appName);
        if (null == specifiedAppFile)
            return null;

        // get contract form specified app
        return FileUtils.getFile(specifiedAppFile, CONTRACTS_DIRECTORY_NAME);
    }


    /**
     * 根据应用名称获取应用下面的文档目录。
     *
     * @param appName
     * @return
     */
    public File getDocumentDirectoryByAppName(String appName) {
        //get specified file by name
        File specifiedAppFile = getSpecifiedAppFileByName(appName);
        if (null == specifiedAppFile)
            return null;

        // get document form specified app
        return FileUtils.getFile(specifiedAppFile, DOCUMENTS_DIRECTORY_NAME);
    }


    /**
     * 根据应用名称获取文档列表。
     *
     * @param appName
     * @return
     */
    public List<AppDocument> getAllDocumentByAppName(String appName) {

        //get specified file by name
        File specifiedAppFile = getSpecifiedAppFileByName(appName);
        if (null == specifiedAppFile)
            return null;

        // get document form specified app
        List<AppDocument> appDocumentList = new ArrayList<>();
        File documentDirectoryFile = FileUtils.getFile(specifiedAppFile, DOCUMENTS_DIRECTORY_NAME);
        if (null == documentDirectoryFile) {
            log.warn("not found document config of app:{}", appName);
            return appDocumentList;
        }

        //get AppDocuments from file
        for (File documentFile : documentDirectoryFile.listFiles()) {
            if (!documentFile.isFile())
                continue;
            AppDocument appDocument = new AppDocument();
            appDocument.setName(documentFile.getName());
            appDocument.setSourceBase64(WebServerTools.convertFileToBase64(documentFile));
            appDocumentList.add(appDocument);
        }

        return appDocumentList;
    }


    /**
     * 根据应用名称获取指定的应用文件目录。
     *
     * @param appName
     * @return
     */
    private File getSpecifiedAppFileByName(String appName) {
        //get all app config
        List<File> appFileList = getAllAppFileFromMarketConfig();//获取所有应用配置
        if (CollectionUtil.isEmpty(appFileList)) {
            log.warn("There are no examples in the app market");
            return null;
        }

        //get app by name
        File specifiedAppFile = appFileList.stream().filter(f -> appName.equalsIgnoreCase(f.getName())).findFirst().orElse(null);
        if (null == specifiedAppFile)
            log.warn("not found specified app from remark by name:{}", appName);

        return specifiedAppFile;
    }


    /**
     * 从应用目录中提取应用信息。
     *
     * @param appFile
     * @return
     */
    private AppInfo getAppInfoFromFile(File appFile) {
        AppInfo appInfo = new AppInfo();
        // get app name
        appInfo.setName(appFile.getName());

        //get logo file as base64
        File logoFile = FileUtils.getFile(appFile, LOGO_FILE_NAME);
        appInfo.setLogoBase64(WebServerTools.convertFileToBase64(logoFile));

        //get remark config
        File remarkFile = FileUtils.getFile(appFile, REMARK_JSON_FILE_NAME);
        try {
            if (remarkFile != null) {
                String remarkJson = FileUtils.readFileToString(remarkFile, Charset.defaultCharset());
                if (StringUtils.isNotBlank(remarkJson)) {
                    AppRemarkConfig appRemarkConfig = JsonUtils.stringToObj(remarkJson, AppRemarkConfig.class);
                    appInfo.setSummarize(appRemarkConfig.getSummarize());
                    appInfo.setDetail(appRemarkConfig.getDetail());
                }
            }
        } catch (Exception ex) {
            log.error("read remark.json file exception", ex);
        }

        return appInfo;
    }

    /**
     * 从应用市场的配置中获取所有应用目录
     *
     * @return
     */
    private List<File> getAllAppFileFromMarketConfig() {
        File appMarketFile = getAppMarketFile();
        if (null == appMarketFile)
            return null;
        List<File> appFileList = Arrays.asList(appMarketFile.listFiles());
        List<File> directoryFileList = null;
        if (CollectionUtil.isNotEmpty(appFileList))
            directoryFileList = appFileList.stream().filter(f -> f.isDirectory()).collect(Collectors.toList());

        return directoryFileList;
    }

    /**
     * 获取应用市场配置目录。
     *
     * @return
     */
    private File getAppMarketFile() {
        File file = null;
        try {
            file = new File(ResourceUtils.getURL(config.getAppMarketBasePath()).getPath());
        } catch (FileNotFoundException e) {
            log.error("path of appMarket is not valid,please check config. appMarketBasePath:{}", config.getAppMarketBasePath(), e);
            return null;
        }
        if (!file.exists()) {
            log.error("path of appMarket is not valid,please check config. appMarketBasePath:{}", config.getAppMarketBasePath());
            return null;
        }

        if (!file.isDirectory()) {
            log.error("path of appMarket is not a directory,please check config. appMarketBasePath:{}", config.getAppMarketBasePath());
            return null;
        }

        return file;
    }


    /**
     * @param appFile
     * @return
     */
    private boolean isAppInfoAllConfigSuccess(File appFile) {
        //check logo
        File logoFile = FileUtils.getFile(appFile, LOGO_FILE_NAME);
        if (logoFile == null || !logoFile.exists()) {
            log.error("fail to check logoFile of appInfo:{} ,because logoFile is null");
            return false;
        }
        long sizeAsMB = FileUtils.sizeOf(logoFile) / (1024 * 1024);
        if (sizeAsMB > config.getAppMarketLogoSizeMax()) {
            log.error("fail to check logoFile of appInfo:{} ,because size of logoFile is:{} , but max support:{}", appFile.getName(), sizeAsMB, config.getAppMarketLogoSizeMax());
            return false;
        }


        //check remark config
        File remarkFile = FileUtils.getFile(appFile, REMARK_JSON_FILE_NAME);
        if (remarkFile == null || !remarkFile.exists()) {
            log.error("fail to check remarkFile of appInfo:{} ,because remarkFile is null", appFile.getName());
            return false;
        }
        sizeAsMB = FileUtils.sizeOf(remarkFile) / (1024 * 1024);
        if (sizeAsMB > config.getAppMarketRemarkFileSizeMax()) {
            log.error("fail to check remarkFile of appInfo:{} ,because size of remarkFile is:{}, but max support:{}", appFile.getName(), sizeAsMB, config.getAppMarketRemarkFileSizeMax());
            return false;
        }


        //check document directory
        File documentDirectoryFile = FileUtils.getFile(appFile, DOCUMENTS_DIRECTORY_NAME);
        if (documentDirectoryFile == null || !documentDirectoryFile.exists()) {
            log.error("fail to check documentDirectoryFile of appInfo:{} ,because documentDirectoryFile is null", appFile.getName());
            return false;
        }
        sizeAsMB = FileUtils.sizeOf(documentDirectoryFile) / (1024 * 1024);
        if (sizeAsMB > config.getAppMarketDocumentDirectorySizeMax()) {
            log.error("fail to check documentDirectoryFile of appInfo:{} ,because size of documentDirectoryFile is:{}, but max support:{}", appFile.getName(), sizeAsMB, config.getAppMarketDocumentDirectorySizeMax());
            return false;
        }


        //check contract directory
        File contractDirectoryFile = FileUtils.getFile(appFile, CONTRACTS_DIRECTORY_NAME);
        if (contractDirectoryFile == null || !contractDirectoryFile.exists()) {
            log.error("fail to check contractDirectoryFile of appInfo:{} ,because contractDirectoryFile is null", appFile.getName());
            return false;
        }
        sizeAsMB = FileUtils.sizeOf(contractDirectoryFile) / (1024 * 1024);
        if (sizeAsMB > config.getAppMarketContractDirectorySizeMax()) {
            log.error("fail to check contractDirectoryFile of appInfo:{} ,because size of contractDirectoryFile is:{}, but max support:{}", appFile.getName(), sizeAsMB, config.getAppMarketContractDirectorySizeMax());
            return false;
        }
        return true;
    }

}
